﻿using System;
using UnityEngine;
using UnityEditor;
using UnityEngine.Assertions;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.IMGUI.Controls;

namespace Hotfix
{
    /// <summary>
    /// 
    /// </summary>
    public static class XHotfixAssetBundleModel
    {
        const string NEW_BUNDLE_BASENAME = "newbundle";
        const string NEW_VARIANT_BASE_NAME = "newvariant";

        /// <summary>
        /// 
        /// </summary>
        public static Color COLOR_LIGHT_GREY = Color.grey * 1.5f;

        /// <summary>
        /// 
        /// </summary>
        private static XHotfixAssetBundleDataSource dataSource;

        /// <summary>
        /// 
        /// </summary>
        private static XHotfixBundleFolderConcreteInfo rootLevelBundles = new XHotfixBundleFolderConcreteInfo("", null);

        /// <summary>
        /// 
        /// </summary>
        private static List<XHotfixAssetBundleMoveData> 
            moveData = new List<XHotfixAssetBundleMoveData>();

        /// <summary>
        /// 
        /// </summary>
        private static List<XHotfixBundleInfo> bundlesToUpdate = new List<XHotfixBundleInfo>();

        /// <summary>
        /// 
        /// </summary>
        private static Dictionary<string, XHotfixAssetBundleAssetInfo> 
            globalAssetList = new Dictionary<string, XHotfixAssetBundleAssetInfo>();

        /// <summary>
        /// 
        /// </summary>
        private static Dictionary<string, HashSet<string>> dependencyTracker = new Dictionary<string, HashSet<string>>();

        /// <summary>
        /// 
        /// </summary>
        private static bool inErrorState = false;
        /// <summary>
        /// 
        /// </summary>
        private static string emptyMessageString;
        
        /// <summary>
        /// 
        /// </summary>
        const string DEFAULT_EMPTY_MESSAGE = "Drag assets here or right-click to begin creating bundles.";
        const string PROBLEM_EMPTY_MESSAGE = "There was a problem parsing the list of bundles. See console.";

        /// <summary>
        /// 
        /// </summary>
        static private Texture2D folderIcon = null;
        static private Texture2D bundleIcon = null;
        static private Texture2D sceneIcon = null;

        /// <summary>
        ///
        /// </summary>
        public static XHotfixAssetBundleDataSource DataSource
        {
            get
            {
                if (dataSource == null)
                {
                    dataSource = new XHotfixAssetDatabaseDataSource ();
                }
            
                return dataSource;
            }

            set { dataSource = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        public static bool Update()
        {
            //this should never do anything. just a safety check.
            bool shouldRepaint = false;
            ExecuteAssetMove(false);

            int size = bundlesToUpdate.Count;
            if (size > 0)
            {
                bundlesToUpdate[size - 1].Update();
                bundlesToUpdate.RemoveAll(item => item.doneUpdating == true);

                if (bundlesToUpdate.Count == 0)
                {
                    shouldRepaint = true;
                    foreach(var bundle in rootLevelBundles.GetChildList())
                    {
                        bundle.RefreshDupeAssetWarning();
                    }
                }
            }

            return shouldRepaint;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tree"></param>
        public static void ForceReloadData(TreeView tree)
        {
            inErrorState = false;
            Rebuild();

            if (tree != null)
                tree.Reload();

            bool doneUpdating = bundlesToUpdate.Count == 0;
            EditorUtility.DisplayProgressBar("Updating Bundles", "", 0);

            int fullBundleCount = bundlesToUpdate.Count;
            while (!doneUpdating && !inErrorState)
            {
                int currCount = bundlesToUpdate.Count;
                EditorUtility.DisplayProgressBar("Updating Bundles", bundlesToUpdate[currCount-1].displayName, (float)(fullBundleCount- currCount) / (float)fullBundleCount);
                doneUpdating = Update();
            }

            EditorUtility.ClearProgressBar();
        }
        
        /// <summary>
        /// Clears and rebuilds model data.  
        /// </summary>
        public static void Rebuild()
        {
            rootLevelBundles = new XHotfixBundleFolderConcreteInfo("", null);
            moveData = new List<XHotfixAssetBundleMoveData>();
            bundlesToUpdate = new List<XHotfixBundleInfo>();
            globalAssetList = new Dictionary<string, XHotfixAssetBundleAssetInfo>();

            Refresh();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bundles"></param>
        public static void AddBundlesToUpdate(IEnumerable<XHotfixBundleInfo> bundles)
        {
            foreach(var bundle in bundles)
            {
                bundle.ForceNeedUpdate();
                bundlesToUpdate.Add(bundle);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public static bool Refresh()
        {
            emptyMessageString = PROBLEM_EMPTY_MESSAGE;
            if (inErrorState)
                return false;

            var bundleList = ValidateBundleList();
            if(bundleList != null)
            {
                emptyMessageString = DEFAULT_EMPTY_MESSAGE;
                foreach (var bundleName in bundleList)
                {
                    AddBundleToModel(bundleName);
                }

                AddBundlesToUpdate(rootLevelBundles.GetChildList());
            }

            if(inErrorState)
            {
                rootLevelBundles = new XHotfixBundleFolderConcreteInfo("", null);
                emptyMessageString = PROBLEM_EMPTY_MESSAGE;
            }

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static string[] ValidateBundleList()
        {
            var bundleList = DataSource.GetAllAssetBundleNames();

            bool valid = true;
            HashSet<string> bundleSet = new HashSet<string>();

            int index = 0;
            bool attemptedBundleReset = false;

            while(index < bundleList.Length)
            {
                var name = bundleList[index];
                if (!bundleSet.Add(name))
                {
                    LogError("Two bundles share the same name: " + name);
                    valid = false;
                }

                int lastDot = name.LastIndexOf('.');
                if (lastDot > -1)
                {
                    var bunName = name.Substring(0, lastDot);
                    var extraDot = bunName.LastIndexOf('.');
                    if(extraDot > -1)
                    {
                        if(attemptedBundleReset)
                        {
                            var message = "Bundle name '" + bunName + "' contains a period.";
                            message += "  Internally Unity keeps 'bundleName' and 'variantName' separate, but externally treat them as 'bundleName.variantName'.";
                            message += "  If a bundleName contains a period, the build will (probably) succeed, but this tool cannot tell which portion is bundle and which portion is variant.";

                            LogError(message);

                            valid = false;
                        }
                        else
                        {
                            if (!DataSource.IsReadOnly ())
                            {
                                DataSource.RemoveUnusedAssetBundleNames();
                            }

                            index = 0;
                            bundleSet.Clear();
                            bundleList = DataSource.GetAllAssetBundleNames();
                            attemptedBundleReset = true;
                            continue;
                        }
                    }

                    if (bundleList.Contains(bunName))
                    {
                        //there is a bundle.none and a bundle.variant coexisting.  Need to fix that or return an error.
                        if (attemptedBundleReset)
                        {
                            valid = false;
                            var message = "Bundle name '" + bunName + "' exists without a variant as well as with variant '" + name.Substring(lastDot+1) + "'.";
                            message += " That is an illegal state that will not build and must be cleaned up.";

                            LogError(message);
                        }
                        else
                        {
                            if (!DataSource.IsReadOnly ())
                            {
                                DataSource.RemoveUnusedAssetBundleNames();
                            }

                            index = 0;
                            bundleSet.Clear();
                            bundleList = DataSource.GetAllAssetBundleNames();
                            attemptedBundleReset = true;

                            continue;
                        }
                    }
                }

                index++;
            }

            return valid ? bundleList : null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static bool BundleListIsEmpty()
        {
            return (rootLevelBundles.GetChildList().Count() == 0);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static string GetEmptyMessage()
        {
            return emptyMessageString;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="newName"></param>
        /// <returns></returns>
        public static XHotfixBundleInfo CreateEmptyBundle(XHotfixBundleFolderInfo folder = null, string newName = null)
        {
            if ((folder as XHotfixBundleVariantFolderInfo) != null)
                return CreateEmptyVariant(folder as XHotfixBundleVariantFolderInfo);

            folder = (folder == null) ? rootLevelBundles : folder;
            string name = GetUniqueName(folder, newName);

            XHotfixBundleNameData nameData;
            nameData = new XHotfixBundleNameData(folder.bundleNameData.bundleName, name);
            
            return AddBundleToFolder(folder, nameData);
        }

        public static XHotfixBundleInfo CreateEmptyVariant(XHotfixBundleVariantFolderInfo folder)
        {
            string name = GetUniqueName(folder, NEW_VARIANT_BASE_NAME);
            string variantName = folder.bundleNameData.bundleName + "." + name;
            XHotfixBundleNameData nameData = new XHotfixBundleNameData(variantName);
            return AddBundleToFolder(folder.parent, nameData);
        }

        public static XHotfixBundleFolderInfo CreateEmptyBundleFolder(XHotfixBundleFolderConcreteInfo folder = null)
        {
            folder = (folder == null) ? rootLevelBundles : folder;
            string name = GetUniqueName(folder) + "/dummy";
            XHotfixBundleNameData nameData = new XHotfixBundleNameData(folder.bundleNameData.bundleName, name);
            return AddFoldersToBundle(rootLevelBundles, nameData);
        }

        public static XHotfixBundleInfo AddBundleToModel(string name)
        {
            if (name == null)
                return null;
            
            XHotfixBundleNameData nameData = new XHotfixBundleNameData(name);

            XHotfixBundleFolderInfo folder = AddFoldersToBundle(rootLevelBundles, nameData);
            XHotfixBundleInfo currInfo = AddBundleToFolder(folder, nameData);

            return currInfo;
        }

        public static XHotfixBundleFolderConcreteInfo AddFoldersToBundle(XHotfixBundleFolderInfo root, XHotfixBundleNameData nameData)
        {
            XHotfixBundleInfo currInfo = root;
            var folder = currInfo as XHotfixBundleFolderConcreteInfo;
            var size = nameData.pathTokens.Count;
            for (var index = 0; index < size; index++)
            {
                if (folder != null)
                {
                    currInfo = folder.GetChild(nameData.pathTokens[index]);
                    if (currInfo == null)
                    {
                        currInfo = new XHotfixBundleFolderConcreteInfo(nameData.pathTokens, index + 1, folder);
                        folder.AddChild(currInfo);
                    }

                    folder = currInfo as XHotfixBundleFolderConcreteInfo;
                    if (folder == null)
                    {
                        inErrorState = true;
                        LogFolderAndBundleNameConflict(currInfo.bundleNameData.fullNativeName);
                        break;
                    }
                }
            }
            return currInfo as XHotfixBundleFolderConcreteInfo;
        }

        public static void LogFolderAndBundleNameConflict(string name)
        {
            var message = "Bundle '";
            message += name;
            message += "' has a name conflict with a bundle-folder.";
            message += "Display of bundle data and building of bundles will not work.";
            message += "\nDetails: If you name a bundle 'x/y', then the result of your build will be a bundle named 'y' in a folder named 'x'.  You thus cannot also have a bundle named 'x' at the same level as the folder named 'x'.";
            LogError(message);
        }

        public static XHotfixBundleInfo AddBundleToFolder(XHotfixBundleFolderInfo root, XHotfixBundleNameData nameData)
        {
            XHotfixBundleInfo currInfo = root.GetChild(nameData.shortName);
            if (!System.String.IsNullOrEmpty(nameData.variant))
            {
                if(currInfo == null)
                {
                    currInfo = new XHotfixBundleVariantFolderInfo(nameData.bundleName, root);
                    root.AddChild(currInfo);
                }
                var folder = currInfo as XHotfixBundleVariantFolderInfo;
                if (folder == null)
                {
                    var message = "Bundle named " + nameData.shortName;
                    message += " exists both as a standard bundle, and a bundle with variants.  ";
                    message += "This message is not supported for display or actual bundle building.  ";
                    message += "You must manually fix bundle naming in the inspector.";
                    
                    LogError(message);
                    return null;
                }
                
                
                currInfo = folder.GetChild(nameData.variant);
                if (currInfo == null)
                {
                    currInfo = new XHotfixBundleVariantDataInfo(nameData.fullNativeName, folder);
                    folder.AddChild(currInfo);
                }
                
            }
            else
            {
                if (currInfo == null)
                {
                    currInfo = new XHotfixBundleDataInfo(nameData.fullNativeName, root);
                    root.AddChild(currInfo);
                }
                else
                {
                    var dataInfo = currInfo as XHotfixBundleDataInfo;
                    if (dataInfo == null)
                    {
                        inErrorState = true;
                        LogFolderAndBundleNameConflict(nameData.fullNativeName);
                    }
                }
            }
            return currInfo;
        }

        public static string GetUniqueName(XHotfixBundleFolderInfo folder, string suggestedName = null)
        {
            suggestedName = (suggestedName == null) ? NEW_BUNDLE_BASENAME : suggestedName;
            string name = suggestedName;
            int index = 1;
            bool foundExisting = (folder.GetChild(name) != null);
            while (foundExisting)
            {
                name = suggestedName + index;
                index++;
                foundExisting = (folder.GetChild(name) != null);
            }
            return name;
        }

        public static XHotfixAssetBundleTreeItem CreateBundleTreeView()
        {
            return rootLevelBundles.CreateTreeView(-1);
        }

        public static XHotfixAssetTreeViewItem CreateAssetListTreeView(IEnumerable<XHotfixBundleInfo> selectedBundles)
        {
            var root = new XHotfixAssetTreeViewItem();
            if (selectedBundles != null)
            {
                foreach (var bundle in selectedBundles)
                {
                    bundle.AddAssetsToNode(root);
                }
            }
            return root;
        }

        public static bool HandleBundleRename(XHotfixAssetBundleTreeItem item, string newName)
        {
            var originalName = new XHotfixBundleNameData(item.bundle.bundleNameData.fullNativeName);

            var findDot = newName.LastIndexOf('.');
            var findSlash = newName.LastIndexOf('/');
            var findBSlash = newName.LastIndexOf('\\');
            if (findDot == 0 || findSlash == 0 || findBSlash == 0)
                return false; //can't start a bundle with a / or .

            bool result = item.bundle.HandleRename(newName, 0);

            if (findDot > 0 || findSlash > 0 || findBSlash > 0)
            {
                item.bundle.parent.HandleChildRename(newName, string.Empty);
            }

            ExecuteAssetMove();

            var node = FindBundle(originalName);
            if (node != null)
            {
                var message = "Failed to rename bundle named: ";
                message += originalName.fullNativeName;
                message += ".  Most likely this is due to the bundle being assigned to a folder in your Assets directory, AND that folder is either empty or only contains assets that are explicitly assigned elsewhere.";
                Debug.LogError(message);
            }

            return result;  
        }

        public static void HandleBundleReparent(IEnumerable<XHotfixBundleInfo> bundles, XHotfixBundleFolderInfo parent)
        {
            parent = (parent == null) ? rootLevelBundles : parent;
            foreach (var bundle in bundles)
            {
                bundle.HandleReparent(parent.bundleNameData.bundleName, parent);
            }
            ExecuteAssetMove();
        }

        public static void HandleBundleMerge(IEnumerable<XHotfixBundleInfo> bundles, XHotfixBundleDataInfo target)
        {
            foreach (var bundle in bundles)
            {
                bundle.HandleDelete(true, target.bundleNameData.bundleName, target.bundleNameData.variant);
            }
            ExecuteAssetMove();
        }

        public static void HandleBundleDelete(IEnumerable<XHotfixBundleInfo> bundles)
        {
            var nameList = new List<XHotfixBundleNameData>();
            foreach (var bundle in bundles)
            {
                nameList.Add(bundle.bundleNameData);
                bundle.HandleDelete(true);
            }
            ExecuteAssetMove();

            //check to see if any bundles are still there...
            foreach(var name in nameList)
            {
                var node = FindBundle(name);
                if(node != null)
                {
                    var message = "Failed to delete bundle named: ";
                    message += name.fullNativeName;
                    message += ".  Most likely this is due to the bundle being assigned to a folder in your Assets directory, AND that folder is either empty or only contains assets that are explicitly assigned elsewhere.";
                    Debug.LogError(message);
                }
            }
        }

        public static XHotfixBundleInfo FindBundle(XHotfixBundleNameData name)
        {
            XHotfixBundleInfo currNode = rootLevelBundles;
            foreach (var token in name.pathTokens)
            {
                if(currNode is XHotfixBundleFolderInfo)
                {
                    currNode = (currNode as XHotfixBundleFolderInfo).GetChild(token);
                    if (currNode == null)
                        return null;
                }
                else
                {
                    return null;
                }
            }

            if(currNode is XHotfixBundleFolderInfo)
            {
                currNode = (currNode as XHotfixBundleFolderInfo).GetChild(name.shortName);
                if(currNode is XHotfixBundleVariantFolderInfo)
                {
                    currNode = (currNode as XHotfixBundleVariantFolderInfo).GetChild(name.variant);
                }
                return currNode;
            }
            else
            {
                return null;
            }
        }

        public static XHotfixBundleInfo HandleDedupeBundles(IEnumerable<XHotfixBundleInfo> bundles, bool onlyOverlappedAssets)
        {
            var newBundle = CreateEmptyBundle();
            HashSet<string> dupeAssets = new HashSet<string>();
            HashSet<string> fullAssetList = new HashSet<string>();

            //if they were just selected, then they may still be updating.
            bool doneUpdating = bundlesToUpdate.Count == 0;
            while (!doneUpdating)
                doneUpdating = Update();

            foreach (var bundle in bundles)
            {
                foreach (var asset in bundle.GetDependencies())
                {
                    if (onlyOverlappedAssets)
                    {
                        if (!fullAssetList.Add(asset.fullAssetName))
                            dupeAssets.Add(asset.fullAssetName);
                    }
                    else
                    {
                        if (asset.IsMessageSet(XHotfixMessageSystem.MessageFlag.AssetsDuplicatedInMultBundles))
                            dupeAssets.Add(asset.fullAssetName);
                    }
                }
            }

            if (dupeAssets.Count == 0)
                return null;
            
            MoveAssetToBundle(dupeAssets, newBundle.bundleNameData.bundleName, string.Empty);
            ExecuteAssetMove();
            return newBundle;
        }

        public static XHotfixBundleInfo HandleConvertToVariant(XHotfixBundleDataInfo bundle)
        {
            bundle.HandleDelete(true, bundle.bundleNameData.bundleName, NEW_VARIANT_BASE_NAME);
            ExecuteAssetMove();
            var root = bundle.parent.GetChild(bundle.bundleNameData.shortName) as XHotfixBundleVariantFolderInfo;

            if (root != null)
                return root.GetChild(NEW_VARIANT_BASE_NAME);
            else
            {
                //we got here because the converted bundle was empty.
                var vfolder = new XHotfixBundleVariantFolderInfo(bundle.bundleNameData.bundleName, bundle.parent);
                var vdata = new XHotfixBundleVariantDataInfo(bundle.bundleNameData.bundleName + "." + NEW_VARIANT_BASE_NAME, vfolder);
                bundle.parent.AddChild(vfolder);
                vfolder.AddChild(vdata);
                return vdata;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public class XHotfixAssetBundleMoveData
        {
            public string assetName;
            public string bundleName;
            public string variantName;

            /// <summary>
            /// 
            /// </summary>
            /// <param name="asset"></param>
            /// <param name="bundle"></param>
            /// <param name="variant"></param>
            public XHotfixAssetBundleMoveData(string asset, string bundle, string variant)
            {
                assetName = asset;
                bundleName = bundle;
                variantName = variant;
            }

            /// <summary>
            /// 
            /// </summary>
            public void Apply()
            {
                if (!DataSource.IsReadOnly ())
                {
                    DataSource.SetAssetBundleNameAndVariant(assetName, bundleName, variantName);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="asset"></param>
        /// <param name="bundleName"></param>
        /// <param name="variant"></param>
        public static void MoveAssetToBundle(XHotfixAssetBundleAssetInfo asset, string bundleName, string variant)
        {
            moveData.Add(new XHotfixAssetBundleMoveData(asset.fullAssetName, bundleName, variant));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assetName"></param>
        /// <param name="bundleName"></param>
        /// <param name="variant"></param>
        public static void MoveAssetToBundle(string assetName, string bundleName, string variant)
        {
            moveData.Add(new XHotfixAssetBundleMoveData(assetName, bundleName, variant));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assets"></param>
        /// <param name="bundleName"></param>
        /// <param name="variant"></param>
        public static void MoveAssetToBundle(IEnumerable<XHotfixAssetBundleAssetInfo> assets, string bundleName, string variant)
        {
            foreach (var asset in assets)
                MoveAssetToBundle(asset, bundleName, variant);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assetNames"></param>
        /// <param name="bundleName"></param>
        /// <param name="variant"></param>
        public static void MoveAssetToBundle(IEnumerable<string> assetNames, string bundleName, string variant)
        {
            foreach (var assetName in assetNames)
                MoveAssetToBundle(assetName, bundleName, variant);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="forceAct"></param>
        public static void ExecuteAssetMove(bool forceAct=true)
        {
            var size = moveData.Count;
            if(forceAct)
            {
                if (size > 0)
                {
                    bool autoRefresh = EditorPrefs.GetBool("kAutoRefresh");
                    EditorPrefs.SetBool("kAutoRefresh", false);

                    AssetDatabase.StartAssetEditing();
                    EditorUtility.DisplayProgressBar("Moving assets to bundles", "", 0);
                    
                    for (int i = 0; i < size; i++)
                    {
                        EditorUtility.DisplayProgressBar("Moving assets to bundle " + moveData[i].bundleName, System.IO.Path.GetFileNameWithoutExtension(moveData[i].assetName), (float)i / (float)size);
                        moveData[i].Apply();
                    }

                    EditorUtility.ClearProgressBar();

                    AssetDatabase.StopAssetEditing();
                    EditorPrefs.SetBool("kAutoRefresh", autoRefresh);

                    moveData.Clear();
                }

                if (!DataSource.IsReadOnly ())
                {
                    DataSource.RemoveUnusedAssetBundleNames();
                }

                Refresh();
            }
        }

        /// <summary>
        /// this version of CreateAsset is only used for dependent assets. 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parent"></param>
        /// <returns></returns>
        public static XHotfixAssetBundleAssetInfo CreateAsset(string name, XHotfixAssetBundleAssetInfo parent)
        {
            if (ValidateAsset(name))
            {
                var bundleName = GetBundleName(name); 
                return CreateAsset(name, bundleName, parent);
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="bundleName"></param>
        /// <returns></returns>
        public static XHotfixAssetBundleAssetInfo CreateAsset(string name, string bundleName)
        {
            if(ValidateAsset(name))
            {
                return CreateAsset(name, bundleName, null);
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="bundleName"></param>
        /// <param name="parent"></param>
        /// <returns></returns>
        public static XHotfixAssetBundleAssetInfo CreateAsset(string name, string bundleName, XHotfixAssetBundleAssetInfo parent)
        {
            if(!System.String.IsNullOrEmpty(bundleName))
            {
                return new XHotfixAssetBundleAssetInfo(name, bundleName);
            }
            else
            {
                XHotfixAssetBundleAssetInfo info = null;
                if(!globalAssetList.TryGetValue(name, out info))
                {
                    info = new XHotfixAssetBundleAssetInfo(name, string.Empty);
                    globalAssetList.Add(name, info);
                }
                info.AddParent(parent.displayName);
                return info;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static bool ValidateAsset(string name)
        {
            if (!name.StartsWith("Assets/"))
                return false;

            string ext = System.IO.Path.GetExtension(name);
            if (ext == ".dll" || ext == ".cs" || ext == ".meta" || ext == ".js" || ext == ".boo")
                return false;

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="asset"></param>
        /// <returns></returns>
        public static string GetBundleName(string asset)
        {
            return DataSource.GetAssetBundleName (asset);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="asset"></param>
        /// <param name="bundle"></param>
        /// <returns></returns>
        public static int RegisterAsset(XHotfixAssetBundleAssetInfo asset, string bundle)
        {
            if(dependencyTracker.ContainsKey(asset.fullAssetName))
            {
                dependencyTracker[asset.fullAssetName].Add(bundle);
                int count = dependencyTracker[asset.fullAssetName].Count;
                if (count > 1)
                    asset.SetMessageFlag(XHotfixMessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, true);

                return count;
            }

            var bundles = new HashSet<string>();
            bundles.Add(bundle);
            
            dependencyTracker.Add(asset.fullAssetName, bundles);
            return 1;            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="asset"></param>
        /// <param name="bundle"></param>
        public static bool UnRegisterAsset(XHotfixAssetBundleAssetInfo asset, string bundle)
        {
            if (dependencyTracker == null || asset == null)
                return false;

            if (dependencyTracker.ContainsKey(asset.fullAssetName))
            {
                dependencyTracker[asset.fullAssetName].Remove(bundle);
                int count = dependencyTracker[asset.fullAssetName].Count;
                switch (count)
                {
                    case 0:
                        dependencyTracker.Remove(asset.fullAssetName);
                        break;

                    case 1:
                        asset.SetMessageFlag(XHotfixMessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, false);
                        break;
                }
            }

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="asset"></param>
        /// <returns></returns>
        public static IEnumerable<string> CheckDependencyTracker(XHotfixAssetBundleAssetInfo asset)
        {
            if (dependencyTracker.ContainsKey(asset.fullAssetName))
            {
                return dependencyTracker[asset.fullAssetName];
            }

            return new HashSet<string>();
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public static void LogError(string message)
        {
            Debug.LogError("Hotfix Bundle Browser: " + message);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public static void LogWarning(string message)
        {
            Debug.LogWarning("Hotfix Bundle Browser: " + message);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static Texture2D GetFolderIcon()
        {
            if (folderIcon == null)
                FindBundleIcons();

            return folderIcon;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static Texture2D GetBundleIcon()
        {
            if (bundleIcon == null)
                FindBundleIcons();

            return bundleIcon;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static Texture2D GetSceneIcon()
        {
            if (sceneIcon == null)
                FindBundleIcons();

            return sceneIcon;
        }

        /// <summary>
        /// 
        /// </summary>
        public static void FindBundleIcons()
        {
            folderIcon = EditorGUIUtility.FindTexture("Folder Icon");

            var packagePath = System.IO.Path.GetFullPath("Packages/com.unity.assetbundlebrowser");
            if (System.IO.Directory.Exists(packagePath))
            {
                bundleIcon = (Texture2D)AssetDatabase.LoadAssetAtPath("Packages/com.unity.assetbundlebrowser/Editor/Icons/ABundleBrowserIconY1756Basic.png", typeof(Texture2D));
                sceneIcon = (Texture2D)AssetDatabase.LoadAssetAtPath("Packages/com.unity.assetbundlebrowser/Editor/Icons/ABundleBrowserIconY1756Scene.png", typeof(Texture2D));
            }
        }
    }
}
